#pragma once
// 定义一个格式化模块把日志信息中的各个元素格式成字符串输出
// 可以让用户自定义输出哪些元素
#include <vector>
#include <cassert>
#include <fstream>
#include <unordered_map>
#include "LogMessage.hpp"
#include "Level.hpp"

namespace Format
{
    // 定义一个抽象基类提供方法
    class FormatItem
    {
    public:
        virtual void format(std::ostream &out, LogMessage::LogMeg &lmsg) = 0;
    };

    // 格式输出日志时间
    class TimeFormat : public FormatItem
    {
    public:
        void format(std::ostream &out, LogMessage::LogMeg &lmsg) override
        {
            struct tm timer;
            localtime_r(&lmsg._timestamp, &timer); // 线程安全的把时间戳转为当地的时间

            char timebuffer[128] = {0};
            // 把时间按照指定格式格式成字符串输出到指定buffer中
            // 2022-02-22 12：20：34
            snprintf(timebuffer, sizeof(timebuffer), "%04d-%02d-%02d %02d:%02d:%02d",
                     timer.tm_year + 1900, timer.tm_mon + 1, timer.tm_mday, timer.tm_hour, timer.tm_min,
                     timer.tm_sec);
            out << timebuffer;
        }
    };
    // 格式输出日志等级
    class LevelFormat : public FormatItem
    {
    public:
        void format(std::ostream &out, LogMessage::LogMeg &lmsg) override
        {
            out << LogLevel::LevelToStr(lmsg._level);
        }
    };

    // 日志行号
    class LineFormat : public FormatItem
    {
    public:
        void format(std::ostream &out, LogMessage::LogMeg &lmsg) override
        {
            out << lmsg._line;
        }
    };

    // 日志所属的文件名称
    class FileFormat : public FormatItem
    {
    public:
        void format(std::ostream &out, LogMessage::LogMeg &lmsg) override
        {
            out << lmsg._file;
        }
    };

    // 日志所属线程ID
    class ThreadIDFormat : public FormatItem
    {
    public:
        void format(std::ostream &out, LogMessage::LogMeg &lmsg) override
        {
            out << lmsg._tid;
        }
    };

    // 日志的日志器名称
    class LoggerFormat : public FormatItem
    {
    public:
        void format(std::ostream &out, LogMessage::LogMeg &lmsg) override
        {
            out << lmsg._Logger;
        }
    };
    // 日志信息主体
    class MessageFormat : public FormatItem
    {
    public:
        void format(std::ostream &out, LogMessage::LogMeg &lmsg) override
        {
            out << lmsg._Log;
        }
    };
    // 制表符
    class TableFormat : public FormatItem
    {
    public:
        void format(std::ostream &out, LogMessage::LogMeg &lmsg) override
        {
            out << "\t";
        }
    };
    // 换行符
    class NewLineFormat : public FormatItem
    {
    public:
        void format(std::ostream &out, LogMessage::LogMeg &lmsg) override
        {
            out << "\n";
        }
    };

    // 其它字符串内容
    class OtherFormat : public FormatItem
    {
    public:
        OtherFormat(const std::string &Otherstr) : _OtherStr(Otherstr)
        {
        }
        void format(std::ostream &out, LogMessage::LogMeg &lmsg) override
        {
            out << _OtherStr;
        }

    private:
        std::string _OtherStr;
    };

    // 提供一个格式化工具类按照用户给定的格式
    // 创建不同的格式化字符串
    //  %d 表示日志时间 %t 线程ID %c日志器名称 %f 文件名称
    //   %l 行号     %p日志等级 %T 制表符 %m消息主体 %n 换行符
    class ForMatter
    {
    public:
        ForMatter(const std::string &fmtstr = "[%d][%t][%c][%f][%l][%p]{%m}%T%n") : _formatstr(fmtstr)
        {
            // 记录格式化字符
            FormatMap['d'];
            FormatMap['t'];
            FormatMap['c'];
            FormatMap['f'];
            FormatMap['l'];
            FormatMap['p'];
            FormatMap['T'];
            FormatMap['n'];
            FormatMap['m'];
            // 初始化必须解析格式字符串成功
            assert(AnalyzeFmStr());
        }
        // 把日志不同的元素按照一定的顺序和格式打印到输出流
        void format(std::ostream &out, LogMessage::LogMeg &lmsg)
        {
            for (auto &item : _items)
            {
                item->format(out, lmsg);
            }
        }
        std::string format(LogMessage::LogMeg &lmsg)
        {
            std::stringstream ss;
            format(ss, lmsg);
            return ss.str();
        }

    private:
        // 解析用户传进来的格式化字符串
        bool AnalyzeFmStr()
        {
            size_t pos = 0, size = _formatstr.size();
            std::string other;
            while (pos < size)
            {
                // 1 如果当前字符不是%
                if (_formatstr[pos] != '%')
                {
                    other.push_back(_formatstr[pos++]);
                    continue;
                }

                // 2走下来就是%,在判断是否是转义%
                if ((pos + 1) < size && _formatstr[pos + 1] == '%')
                {
                    other.push_back(_formatstr[pos + 1]);
                    pos += 2;
                    continue;
                }
                // 3 走到这里就是% 匹配格式符号
                // [%d][%t][%c][%f][%l][%p]%T%n%
                if ((pos + 1) < size && _formatstr[pos + 1] != '%')
                {
                    std::string key;
                    key = _formatstr[pos + 1];
                    // 如果其它字符串不为空先添加其它字符串的格式化方法
                    if (!other.empty())
                    {
                        AddItem("o", other);
                        other.clear();
                    }

                    //%g %h不是合法的格式匹配符"[%d][%l],%m%n%ngg%g%
                    //当做普通字符串处理
                    if (!FormatMap.count(_formatstr[pos + 1]))
                    {
                        other.push_back(_formatstr[pos]);
                        other.push_back(_formatstr[pos + 1]);
                        AddItem("o", other);
                    }
                    else
                    {
                        AddItem(key, "");
                    }
                    other.clear();
                    pos += 2;
                    continue;
                }
                // 边缘情况 最后一个字符为%
                if (pos == size - 1)
                {
                    // other.clear();
                    other.push_back(_formatstr[pos]);
                    AddItem("o", other);
                    other.clear();
                    break;
                }
            }
            if (!other.empty())
            {
                AddItem("o", other);
                other.clear();
            }
            return true;
        }

    private:
        // 把解析的格式匹配符转化成对应的格式化方法按照一定方法存储起来
        //%d->日期这个元素格式化成字符串
        void AddItem(const std::string &key, const std::string &value)
        {
            // 如果是%d 就把格式化日期的方法对象存储起来
            if (key == "d")
                _items.push_back(std::make_shared<TimeFormat>());
            else if (key == "t")
                _items.push_back(std::make_shared<ThreadIDFormat>());
            else if (key == "c")
                _items.push_back(std::make_shared<LoggerFormat>());
            else if (key == "f")
                _items.push_back(std::make_shared<FileFormat>());
            else if (key == "l")
                _items.push_back(std::make_shared<LineFormat>());
            else if (key == "p")
                _items.push_back(std::make_shared<LevelFormat>());
            else if (key == "T")
                _items.push_back(std::make_shared<TableFormat>());
            else if (key == "m")
                _items.push_back(std::make_shared<MessageFormat>());
            else if (key == "n")
                _items.push_back(std::make_shared<NewLineFormat>());
            else
                _items.push_back(std::make_shared<OtherFormat>(value));
        }

    private:
        std::string _formatstr; // 用户给定的格式
        // 利用父类指针对象来存储日志中每个不同元素的格式化方法
        std::vector<std::shared_ptr<FormatItem>> _items;
        std::unordered_map<char, int> FormatMap;
    };
}